package org.panxiong.openapi.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;

public class ClassUtils {

    /**
     * 遍历获取target/classes目录下的全部.class文件, 返回文件路径
     */
    public static Set<String> getClassPathList(String directory) throws IOException {
        Set<String> classes = new HashSet<>();
        Stream<Path> paths = Files.walk(Paths.get(directory));
        paths.filter(Files::isRegularFile) // 过滤出常规文件
                .filter(path -> path.toString().endsWith(".class")) // 过滤出.class文件
                .forEach(it -> classes.add(it.toAbsolutePath().toString()));
        return classes;
    }

    /**
     * 遍历获取target/classes目录下的全部.class文件, 返回class名称
     */
    public static Set<String> getClassNameList(String directory) throws IOException {
        Set<String> names = new HashSet<>();
        Set<String> pathList = getClassPathList(directory);
        for (String path : pathList) {
            path = path.replace(directory + File.separator, "");
            path = path.replace(File.separator, ".");
            path = path.replace(".class", "");
            names.add(path);
        }
        return names;
    }

    /**
     * 遍历获取target/classes目录下的全部.class文件, 返回class对象
     */
    public static Set<Class<?>> getClassObjectList(String directory, ClassLoader parent) throws IOException, ClassNotFoundException {
        Set<Class<?>> objects = new HashSet<>();
        URL url = Paths.get(directory).toUri().toURL();
        URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, parent);
        Set<String> names = getClassNameList(directory);
        for (String name : names) {
            objects.add(classLoader.loadClass(name));
        }
        return objects;
    }

    public static boolean isEmpty(String string) {
        return (string == null || string.length() <= 0);
    }

    /**
     * 是否是期望的基础类型
     */
    public static boolean isBaseClass(Class<?> clazz) {
        // 检查是否是基本类型
        if (clazz.isPrimitive()) {
            return true;
        }
        // 检查是否是包装类型
        if (Number.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz)) {
            return true;
        }
        // 检查是否是String
        if (String.class.equals(clazz) || Integer.class.isAssignableFrom(clazz)) {
            return true;
        }
        // 检查是否是枚举类型
        if (clazz.isEnum()) {
            return true;
        }
        // 检查是否是Object
        return Object.class.equals(clazz);
    }

    /**
     * 检查是否是数组  检查是否是集合
     */
    public static boolean isJdkArray(Class<?> clazz) {
        return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
    }

    /**
     * 检查是否是Map
     */
    public static boolean isJdkMap(Class<?> clazz) {
        return Map.class.isAssignableFrom(clazz);
    }

    /**
     * 是否自定义类 如果以上条件都不满足，则认为是普通模型
     */
    public static boolean isCustomModel(Class<?> clazz) {
        if (isBaseClass(clazz)) {
            return false;
        }
        if (isJdkArray(clazz)) {
            return false;
        }
        return !isJdkMap(clazz);
    }

    /**
     * 构建一个实例的默认实现，要求对象必须存在一个无参构造函数, 且不能时基本数据类型
     */
    public static Object newDefaultInstance(Class<?> cls) throws Exception {
        if (!isCustomModel(cls)) {
            return new Exception("必须是自定义类型");
        }
        Object instance = cls.getDeclaredConstructor().newInstance();
        Field[] fields = instance.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (ClassUtils.isJdkArray(field.getType())) {
                field.set(instance, new ArrayList<>());
            } else if (ClassUtils.isJdkMap(field.getType())) {
                field.set(instance, new HashMap<>());
            } else if (String.class.equals(field.getType())) {
                field.set(instance, "");
            } else if (Object.class.equals(field.getType())) {
                field.set(instance, null);
            } else if (fields.getClass().isEnum()) {
                field.set(instance, null);
            } else if (Boolean.class.isAssignableFrom(field.getType())) {
                field.set(instance, false);
            } else if (field.getType().isPrimitive() || Number.class.isAssignableFrom(field.getType()) || Character.class.isAssignableFrom(field.getType())) {
                field.set(instance, 0);
            } else if (isCustomModel(field.getType())) {
                field.set(instance, newDefaultInstance(field.getType()));
            } else {
                // field.set(instance, null); // TODO
                return new Exception("未知的类型：" + field.getType().getName());
            }
        }
        return instance;
    }

    public static ObjectMapper getObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);    // 开启缩进输出
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // 禁用 FAIL_ON_EMPTY_BEANS
        FilterProvider filters = new SimpleFilterProvider().addFilter("nullFilter", null);
        objectMapper.setFilterProvider(filters);
        objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
        return objectMapper;
    }

    /**
     * 获取对象泛型 ResponseEntity<String>
     *
     * @param cls ResponseEntity
     * @return String
     */
    public static Class<?> getObjectGeneric(Class<?> cls) {
        Class<?> targetClass = null;
        Type type = cls.getGenericSuperclass();   // 获取 ResponseEntity 的泛型类型
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            if (actualTypeArguments.length > 0) {
                Type actualTypeArgument = actualTypeArguments[0];
                if (actualTypeArgument instanceof Class) {
                    targetClass = (Class<?>) actualTypeArguments[0];
                }
            }
        }
        return targetClass;
    }

}
